package model;

import java.util.ArrayList;

import org.workplicity.entry.Entry;
import org.workplicity.util.Helper;
import org.workplicity.worklet.WorkletContext;

import com.mongodb.BasicDBObject;
import com.mongodb.util.JSON;

import cc.test.bridge.BudgetInterface;
import cc.test.bridge.LineInterface;
import cc.test.bridge.SublineInterface;

public class Line extends Entry implements LineInterface
{
	private static final long serialVersionUID = 1L;
	protected String repositoryName = "Lines";
	protected String name = null;
	protected Integer number = null;
	protected Integer budgetId = null;
	protected ArrayList<Integer> sublineIds = new ArrayList<Integer>();

	/**
	 * Default no argument constructor
	 */
	public Line()
	{
	}

	/**
	 * Line constructor that takes a budgetId
	 * 
	 * @param budgetId
	 *            The budgetId of the parent budget
	 */
	public Line(Integer budgetId)
	{
		this.budgetId = budgetId;
	}

	@Override
	/**
	 * Getter for name
	 * 
	 * @return The name String
	 */
	public String getName()
	{
		return this.name;
	}

	@Override
	/**
	 * Setter for name
	 * 
	 * @param The String containing the Line name
	 */
	public void setName(String name)
	{
		this.name = name;
		
		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If LineUpdates hamper does not contain the line already
			if (!Hampers.getLineUpdates().containsKey(this.id))
			{
				Hampers.getLineUpdates().put(this.id, this);
			}
		}
	}

	@Override
	/**
	 * Getter for repositoryName
	 * 
	 * @return The repositoryName String
	 */
	public String getRepositoryName()
	{
		return this.repositoryName;
	}

	/**
	 * Setter for repositoryName
	 * 
	 * @param The String containing the repositoryName
	 */
	public void setRepositoryName(String repositoryName)
	{
		this.repositoryName = repositoryName;
	}

	@Override
	/**
	 * Getter for number
	 * 
	 * @return The number Integer
	 */
	public Integer getNumber()
	{
		return this.number;
	}

	/**
	 * Setter for number
	 * 
	 * @param The Integer containing the number
	 */
	public void setNumber(Integer number)
	{
		this.number = number;

		// If created already (it is not in the create hamper)
		if (this.id != -1)
		{
			// If LineUpdates hamper does not contain the line already
			if (!Hampers.getLineUpdates().containsKey(this.id))
			{
				Hampers.getLineUpdates().put(this.id, this);
			}
		}
	}

	/**
	 * Getter for budgetId
	 * 
	 * @return The budgetId Integer
	 */
	public Integer getBudgetId()
	{
		return this.budgetId;
	}

	/**
	 * Setter for budetId
	 * 
	 * @param The Integer containing the budetId
	 */
	public void setBudgetId(Integer budetId)
	{
		this.budgetId = budetId;
	}

	/**
	 * Getter for sublineIds
	 * 
	 * @return The sublineIds ArrayList<Integer>
	 */
	public ArrayList<Integer> getSublineIds()
	{
		return this.sublineIds;
	}

	/**
	 * Setter for sublineIds
	 * 
	 * @param The ArrayList<Integer> containing the sublineIds
	 */
	public void setSublineIds(ArrayList<Integer> sublineIds)
	{
		this.sublineIds = sublineIds;
	}

	/**
	 * Getter for serialVersionUID
	 * 
	 * @return The serialVersionUID long
	 */
	public static long getSerialversionuid()
	{
		return serialVersionUID;
	}

	@Override
	/**
	 * Adds a Subline to the Line.
	 * 
	 * @param subline The Subline to add to the Line
	 */
	public void add(SublineInterface subline)
	{
		if (this.id == -1)
		{
			Integer uid = (int) System.currentTimeMillis();
			((Subline) subline).setParentLineId(uid);
			
			Hampers.getLineUpdates().put(uid, this);
			Hampers.getSublineCreates().put(-1, subline);
		}
		else
		{
			Hampers.getLineUpdates().put(this.id, this);
			Hampers.getSublineCreates().put(-1, subline);
		}
	}

	@Override
	/**
	 * Creates a new Subline whose parentId is this LineId
	 * 
	 * @return The new Subline
	 */
	public SublineInterface createSubline()
	{
		return new Subline(this.id);
	}

	@Override
	/**
	 * Deletes a Subline from the Line
	 * 
	 * @param subline The Subline to remove
	 */
	public void delete(SublineInterface subline)
	{
		if (this.id == -1)
		{
			Integer uid = (int) System.currentTimeMillis();
			((Subline) subline).setParentLineId(uid);
			
			Hampers.getLineUpdates().put(uid, this);
			Hampers.getSublineDeletes().put(-1, subline);
		}
		else
		{
			Hampers.getLineUpdates().put(this.id, this);
			Hampers.getSublineDeletes().put(-1, subline);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	/**
	 * Fetches the Sublines that belong to the Line
	 * 
	 * @return An ArrayList<SublineInterface> containing the SublineInterfaces
	 *         that were fetched.
	 */
	public ArrayList<SublineInterface> fetchSublines()
	{
		ArrayList<SublineInterface> subLines = new ArrayList<SublineInterface>();
		
		for (Integer i : this.getSublineIds())
		{
			BasicDBObject predicate = new BasicDBObject();
			predicate.put("entry.id", i);
			String criteria = JSON.serialize(predicate);
			
			try
			{
				int size = Hampers.getLineUpdates().size();
				ArrayList<SublineInterface> subLine = Helper.query("Sublines",
						criteria, WorkletContext.getInstance());
				
				if (size != Hampers.getLineUpdates().size())
				{
					Hampers.clearUpdates();
				}
				
				for (SublineInterface sub : subLine)
				{
					subLines.add(sub);
				}
				
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return subLines;
	}

	/**
	 * Commits a newly created Lines to the repository
	 * 
	 * @return A boolean of if the Line was successfully committed.
	 */
	public static Boolean commitLineCreates()
	{
		Boolean successful = true;

		for (LineInterface line : Hampers.getLineCreates().values())
		{
			// insert line into Mongo
			try
			{
				successful = Helper.insert((Line) line, "Lines",
						WorkletContext.getInstance());
				
				System.out.println("Created line with id " + line.getId());
			}
			catch (Exception e)
			{
				System.out.println("Error: Could not created line with id "
						+ line.getId());
				
				e.printStackTrace();
				successful = false;
			}

			// Give new Id to all subline children (if any)
			if (((Line) line).getSublineIds().size() > 0)
			{
				for (Integer i : ((Line) line).getSublineIds())
				{
					if (Hampers.getSublineUpdates().containsKey(i))
					{
						((Subline) Hampers.getSublineUpdates().get(i))
								.setParentLineId(line.getId());
					}
				}
			}
			
			// Give new Id to parent Budget
			if (Hampers.getBudgetUpdates().containsKey(
					((Line) line).getBudgetId()))
			{
				BudgetInterface budget = Hampers.getBudgetUpdates().get(
						((Line) line).getBudgetId());
				
				if (!((Budget) budget).getLineIds().contains(line.getId()))
				{
					((Budget) budget).lineIds.add(line.getId());
				}
			}
			
			if (Hampers.getBudgetCreates().containsKey(
					((Line) line).getBudgetId()))
			{
				BudgetInterface budget = Hampers.getBudgetCreates().get(
						((Line) line).getBudgetId());
				
				if (!((Budget) budget).getLineIds().contains(line.getId()))
				{
					((Budget) budget).lineIds.add(line.getId());
				}
			}
		}

		return successful;
	}

	/**
	 * Commits an updated Lline to the repository
	 * 
	 * @return A boolean of if the Line was successfully committed.
	 */
	public static Boolean commitLineUpdates()
	{
		Boolean successful = true;
		
		for (LineInterface line : Hampers.getLineUpdates().values())
		{
			// update line in Mongo
			try
			{
				successful = Helper.insert((Line) line, "Lines",
						WorkletContext.getInstance());
				
				System.out.println("Updated line with id " + line.getId());
			}
			catch (Exception e)
			{
				System.out.println("Error: Could not update line with id "
						+ line.getId());
				e.printStackTrace();
				successful = false;
			}
		}

		return successful;
	}

	/**
	 * Deletes a Line from the repository, as well as all associated Sublines
	 * 
	 * @return A boolean of if the Line was successfully deleted.
	 */
	public static Boolean commitLineDeletes(Line line)
	{
		// Delete all sublines associated
		Boolean successful = true;
		ArrayList<SublineInterface> sublines = line.fetchSublines();

		for (SublineInterface sub : sublines)
		{
			successful = Subline.commitSublineDeletes((Subline) sub);
		}

		if (successful)
		{
			try
			{
				successful = Helper.delete((Line) line, "Lines",
						WorkletContext.getInstance());
				
				System.out.println("Deleted line with id " + line.getId());
			}
			catch (Exception e)
			{
				System.out.println("Error: Could not delete line with id "
						+ line.getId());
				
				e.printStackTrace();
				successful = false;
			}

			if (successful)
			{
				if (Hampers.getBudgetUpdates().containsKey(line.getBudgetId()))
				{
					BudgetInterface budget = Hampers.getBudgetUpdates().get(
							line.getBudgetId());
					
					((Budget) budget).getLineIds().remove(line.id);
				}
				else if (Hampers.getBudgetCreates().containsKey(
						line.getBudgetId()))
				{
					BudgetInterface budget = Hampers.getBudgetCreates().get(
							line.getBudgetId());
					
					((Budget) budget).getLineIds().remove(line.id);
				}
				
				successful = true;
			}
			else
			{
				successful = false;
			}
		}
		else
		{
			System.out.println("Failed to delete all sublines");
			successful = false;
		}

		return successful;
	}

	@Override
	/**
	 * Dummy commit function for LineInterface 
	 */
	public Boolean commit()
	{
		return null;
	}
}